home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
CD ROM Paradise Collection 4
/
CD ROM Paradise Collection 4 1995 Nov.iso
/
program
/
tjgold.zip
/
INSTALL.001
/
GOLDMENU.PAS
< prev
next >
Wrap
Pascal/Delphi Source File
|
1995-07-12
|
71KB
|
2,358 lines
{--------------------------------------------------------------------------}
{ Product: TechnoJock's Turbo Toolkit }
{ Version: GOLD }
{ Build: 1.01 }
{ }
{ Copyright 1986-1995 TechnoJock Software, Inc. }
{ All Rights Reserved }
{ Restricted by License }
{--------------------------------------------------------------------------}
{**********************************}
{** Unit: GOLDMENU **}
{**********************************}
{++++++++++++++++++++++++++++++} unit GOLDMENU; {++++++++++++++++++++++++++++}
{$I GOLDFLAG.INC}
{$IFNDEF GOLDMENU}
{$DEFINE GOLDMENU}
{$ENDIF}
{++++++++++++++++++++++++++++++++} INTERFACE {+++++++++++++++++++++++++++++++}
uses DOS, CRT, GoldAttr, GoldTint, GoldHard, GoldMisc, GoldKey,
GoldFast, GoldStr, GoldWin;
const
MaxChoices = 30;
MenuStrLength = 40; {make longer if necessary}
NoChange: string[3] = '@#$';
type
XPackedWord = record
X1: byte;
X2: byte;
end;
MenuHook = procedure(var Key:word; Choice:integer; var Ecode:integer);
MenuHindHook = procedure(Choice:integer; var Ecode:integer);
MenuRecord = record
Heading1 : string[MenuStrLength]; {'' for no heading}
Heading2 : string[MenuStrLength];
Topic : array[1..MaxChoices] of string[MenuStrLength];
TotalPicks : integer;
PicksPerLine : byte;
AddPrefix : byte; {0 no, 1 No.'s, 2 Lets}
TopLeftXY : array[1..2] of byte; {X,Y}
Boxtype : byte; {0,1,2,3, >3}
Colors : array[1..5] of byte;
Margins : byte;
AllowEsc : boolean; {true if Esc will exit}
Hook : Menuhook;
HindHook : MenuHindHook;
end; {MenuRecord}
{Pull down declarations follow}
PullHook = procedure (ID: integer);
PopUpPtr = ^PopUp;
PullItemPtr = ^PullItem;
PullItem = record
ID: integer; {id returned by menu idenitifying user selection}
Topic: ^string;
LongDesc: ^string;
HK: word; {hotkey}
AHK: word; {alternate hotkey}
Active: boolean;
Status: word; {used for different purposes based upon menu type}
Toggle: boolean;
ChildPopUp: PopUpPtr;
NextPtr: PullItemPtr;
end; {PullItem}
PopUp = record
ActiveItem: byte;
Width: byte;
{internal}
ItemCount: byte;
X1,Y1,X2,Y2: integer;
FirstItem: PullItemPtr;
end; {PopUp}
ChainItemPtr = ^ChainItem;
ChainItem = record
PopUp: PopUpPtr;
NextPtr: ChainItemPtr;
end;
HKPtr = ^HK;
HK = record
HK: word;
ID: integer;
NextPtr: HKPtr;
end;
BarPtr = ^Bar;
Bar = record
TopX: byte;
TopY: byte;
DescX1: byte;
DescX2: byte;
DescY: byte;
Style: byte;
ActiveItem: word;
EraseFullLine: boolean;
{internal - do not access directly}
MainX2: byte;
MainY: byte;
MainCount: byte;
ActiveChain: ChainItemPtr; {list of all menus on display}
FirstItem: PullItemPtr;
HKs: HKPtr; {hot keys that immeditaly return a menu selection}
MenuDown: boolean;
MsgDisplayed: boolean;
HelpHook: PullHook;
HelpActive: boolean;
HelpKey: word;
HelpMargin: byte;
HindHook: PullHook;
end; {Bar}
MenuSetRec = record
LastECode: integer;
EMsgFunc: ErrMsgFunc;
MenuLeft: string[1];
MenuRight: string[1];
PullLeft: string[1];
PullRight: string[1];
PullSubIndicator: string[1];
InactiveChar: char;
TabChar: char;
ToggleChar: char;
ActivateKey: word;
Separator: string[1];
PullStyle: byte;
MsgX1: byte;
MsgX2: byte;
MsgY: byte;
HelpStr: string[20];
Helpkey: word;
end; {MenuSetRec}
function LastMenuError: integer;
procedure NoHook(var Key:word; Choice:integer; var Ecode: integer);
{Standard menu windows}
procedure MenuSet(var M: Menurecord);
procedure DisplayMenu(MenuDef: Menurecord; Window:Boolean; var Choice,Errorcode: integer);
{Pulldown menu creation}
procedure InitBar(var P: Bar);
procedure InitPopup(var M:PopUp);
procedure BarAddItem(var M: Bar; Item:string; ID:integer; HK,AltHK:word; Desc:string; PopUp:pointer);
procedure PopupAddItem(var P:PopUp;Item:string; ID:integer; HK:word; Desc:string; ChildMenu:pointer);
procedure BarAddHK(var P: Bar; K:word; HotID:integer);
{changing menu settings}
procedure BarSetActive(var M: Bar; ID:integer; On: boolean);
procedure BarChangeItem(var M: Bar; Item:string; ID,NewID:integer; HK,AltHK:word; Desc:string; PopUp:pointer);
procedure BarChangeText(var M: Bar; ID:integer; Item,Desc:string);
procedure BarDelItem(var M: Bar; ID:integer);
procedure BarDelHK(var P: Bar; K:word);
procedure PopUpSetActive(var P:PopUp; ID:integer; On: boolean);
procedure PopupChangeItem(var P:PopUp;Item:string; ID,NewID:integer; HK:word; Desc:string; ChildMenu:pointer);
procedure PopUpChangeText(var P:PopUp; ID:integer; Item,Desc:string);
procedure PopUpDelItem(var P:PopUp; ID:integer);
{menu display and activation}
procedure DrawBar(var M: Bar);
function ActivatePullMenu(var P: Bar): integer;
function IsPullKey(var P: Bar; K:word; X,Y:byte):boolean;
function PullPushKey(var M: Bar; K:word; X,Y:byte): integer;
{hooks}
procedure NoPullHook(ID:integer);
procedure AssignMenuHelpHook(var M: Bar; Proc:PullHook);
procedure AssignMenuHindHook(var M: Bar; Proc:PullHook);
procedure RemoveMenuHelpHook(var M: Bar);
procedure RemoveMenuHindHook(var M: Bar);
{menu disposal}
procedure DestroyBar(M:Bar);
procedure DestroyPopUp(P:PopUp);
{$IFDEF TTT5}
procedure Menu_Set(var M: Menurecord);
procedure Display_Menu(MenuDef: Menurecord; Window:Boolean; var Choice,Errorcode: integer);
{$ENDIF}
var
MenuVars: MenuSetRec;
{+++++++++++++++++++++++++++++} IMPLEMENTATION {+++++++++++++++++++++++++++++}
const
GPullLeft = -10;
GPullRight = -11;
{******************************}
{** Miscellaneous Routines **}
{******************************}
{$IFOPT F-}
{$DEFINE FOFF}
{$F+}
{$ENDIF}
procedure NoPullHook(ID:integer);
{}
begin
end; { NoPullHook }
procedure NoHook(var Key:word; Choice:integer; var Ecode: integer);
{}
begin
end; {NoHook}
procedure NoHindHook(Choice:integer; var Ecode:integer);
{}
begin
end; {NoHindHook}
{$IFDEF FOFF}
{$F-}
{$UNDEF FOFF}
{$ENDIF}
{$IFOPT F-}
{$DEFINE FOFF}
{$F+}
{$ENDIF}
function MenuEMsg(ECode:integer): string;
{}
begin
case Ecode of
0: exit;
1001: MenuEMsg := 'Too Many Picks to display. Change PicksPerLine';
1002: MenuEMsg := 'Insufficient memory to create Pull Menu';
1003: MenuEMsg := 'Unable to change Menu item -- ID not found';
else
MenuEMsg := 'Internal Menu error';
end; {case}
end; { MenuEMsg }
{$IFDEF FOFF}
{$F-}
{$UNDEF FOFF}
{$ENDIF}
procedure MenuSetError(ECode:integer);
{}
{$IFOPT D+}
var Msg: string;
{$ENDIF}
begin
MenuVars.LastEcode := ECode;
{$IFOPT D+} {if debug active display an error message and terminate}
if Ecode <> 0 then
begin
str(Ecode,Msg);
Msg := Msg+': '+MenuVars.EMsgFunc(Ecode);
SetWinIgnore(true);
if PromptCustom(' GoldMenu Error ',Msg,' ~I~gnore ',' ~A~bort ','',279,286,0,0, 10000) = 2 then
Halt;
end;
{$ENDIF}
end; {MenuSetError}
function LastMenuError: integer;
{}
begin
LastMenuError := MenuVars.LastECode;
end; { LastMenuError }
procedure MenuSet(var M: Menurecord);
{}
begin
with M do
begin
Heading1 := '';
Heading2 := '';
Topic[1] := '';
TotalPicks := 0;
PicksPerLine := 1;
AddPrefix := 1;
TopLeftXY[1] := 0;
TopLeftXY[2] := 0;
Boxtype := 5;
Colors[1] := Tint[MenuHiHot];
Colors[2] := Tint[MenuHi];
Colors[3] := Tint[MenuNormHot];
Colors[4] := Tint[MenuNorm];
Colors[5] := Tint[MenuBorder];
Margins := 5;
AllowEsc := true;
Hook := NoHook;
HindHook := NoHindHook;
end;
end; {MenuSet}
procedure DisplayMenu(MenuDef: Menurecord;
Window:Boolean;
var Choice,Errorcode : integer);
Const
Alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
Numbers = '123456789';
var
I,J,X2,Y2,headingLines: integer;
TextWidth: byte;
Ecode: integer;
procedure GetDimensions;
var Fullwidth,MaxWidth: integer;
procedure ValidatePrefix;
{
0 no prefix
1 numbers prefix
2 letters prefix
3 function key prefix
4 capital letter selection
}
begin
with MenuDef do
begin
if PicksPerLine < 1 then PicksPerLine := 1;
if (TotalPicks = 10) and (AddPrefix = 1) then
AddPrefix := 3;
if (TotalPicks > 10) and (AddPrefix in [1,3]) then
AddPrefix := 2;
if (Addprefix > 4) or (TotalPicks > 26) or (Addprefix < 0) then
Addprefix := 0;
end; {do}
end; { ValidatePrefix }
procedure AddPrefix;
{}
var I: integer;
begin
with MenuDef do
begin
case AddPrefix of
1: for I := 1 to TotalPicks do
if Topic[I][1] = '-' then
insert(inttostr(I)+' ',Topic[I],2)
else if Topic[I] <> '' then
Topic[I] := '~'+inttostr(I)+'~' + ' ' + Topic[I];
2: for I := 1 to TotalPicks do
if Topic[I][1] = '-' then
insert(Copy(Alphabet,I,1)+' ',Topic[I],2)
else if Topic[I] <> '' then
Topic[I] := '~'+Copy(Alphabet,I,1)+'~' + ' ' + Topic[I];
3: for I := 1 to TotalPicks do
if Topic[I][1] = '-' then
insert('F'+InttoStr(I)+' '+copy(' ',1,ord(TotalPicks=10)),Topic[I],2)
else if Topic[I] <> '' then
Topic[I] := '~F'+InttoStr(I)+'~' + ' ' +copy(' ',1,ord(TotalPicks=10))+ Topic[I]
end; {case}
end; {do}
end; { AddPrefix }
procedure FindLongestTopic;
{}
var
I,J: integer;
L:byte;
begin
with MenuDef do
begin
Textwidth := 0;
for I := 1 to TotalPicks do
begin
L := length(strip('A',HiMarker,Topic[I])) - ord((Topic[I] <> '') and (Topic[I] = '-'));
if L > TextWidth then
Textwidth := L; {find the longest text}
end;
end;
end; { FindLongestTopic }
procedure AdjustTextWidth(Len: integer);
{}
var
I,J : integer;
L: byte;
begin
with MenuDef do
begin
for I := 1 to TotalPicks do
begin
L := length(strip('A',HiMarker,Topic[I]));
if L > Len then {reduce it}
delete(Topic[I],succ(Len),255)
else if L <> 0 then {expand it}
for J := succ(L) to Textwidth do
Topic[I] := Topic[I] + ' ';
end;
end; {do}
end; { AdjustTextWidth }
procedure DetermineMaxWidth;
{findout the max internal menu space - MaxWidth}
begin
with MenuDef do
begin
if Margins < 0 then
Margins := 0;
if not (BoxType in [0..9]) then
BoxType := 0;
MaxWidth := 80 - 2*Margins - 1; {-1 for arrow symbol to left of pick}
case BoxType of
1..4: MaxWidth := MaxWidth - 2; {box sides}
5 : MaxWidth := pred(MaxWidth); {box shadow}
6..9: MaxWidth := MaxWidth - 3; {box sides and shadow}
end;
end;
end; { DetermineMaxWidth }
procedure ValidatePicksPerLine;
{}
begin
with MenuDef do
begin
if succ(TextWidth)*PicksPerLine <= MaxWidth then
exit; {no adjustment necessary, everything fits}
if (TextWidth-2)*PicksPerLine <= Maxwidth then
TextWidth := pred(MaxWidth div PicksperLine)
else
begin
while succ(TextWidth)*PicksPerLine > MaxWidth do
PicksPerLine := pred(PicksPerLine);
if PicksPerLine = 0 then
begin
TextWidth := pred(MaxWidth);
PicksPerLine := 1;
end;
end;
end; {with}
end; { ValidatePicksPerLine }
procedure DetermineXDimensions;
{Checks to see if the menu will fit, if it won't it changes something!}
begin
with MenuDef do
begin
Fullwidth := succ(Textwidth)*PicksPerLine + 2*Margins;
case BoxType of
1..4 : FullWidth := FullWidth + 2; {box sides}
5 : FullWidth := succ(FullWidth); {box shadow}
6..9 : FullWidth := FullWidth + 3; {box sides and shadow}
end; {case}
if TopleftXY[1] < 1 then
TopleftXY[1] := (80 - Fullwidth) div 2;
if TopLeftXY[1] + Fullwidth < 80 then
X2 := TopleftXY[1] + Fullwidth
else
begin
X2 := 80;
TopLeftXY[1] := 80 - Fullwidth + 1;
end;
end; {with}
end; { DetermineXDimensions }
procedure DetermineYDimensions;
{}
var BoxLines,
TopicLines,
FullDepth: integer;
begin
with MenuDef do
begin
TopicLines := TotalPicks div PicksPerLine; {no of full rows of picks}
if TotalPicks mod PicksPerLine > 0 then {+1 if partial row of picks}
TopicLines := succ(TopicLines);
case BoxType of
0 : Boxlines := 0;
1..5: BoxLines := 2; {box sides}
6..9: BoxLines := 3; {box sides and shadow}
end;
HeadingLines := 0;
if length(Heading1) > 0 then
HeadingLines := succ(HeadingLines);
if length(Heading2) > 0 then
HeadingLines := succ(HeadingLines);
if HeadingLines > 0 then {add a line for a gap}
HeadingLines := succ(HeadingLines); {gap above topics}
if BoxType = 5 then
HeadingLines := succ(HeadingLines);
Fulldepth := BoxLines+TopicLines+HeadingLines;
if HeadingLines > 0 then
Fulldepth := succ(Fulldepth); {+1 gap below topics if headings}
if FullDepth > HardVars.Depth then {if it doesn't fit, drop off topics}
begin
if HeadingLines > 0 then
TotalPicks := (HardVars.Depth - BoxLines -HeadingLines-1)*PicksPerLine
else
TotalPicks := (HardVars.Depth - BoxLines - HeadingLines)*PicksPerLine;
FullDepth := 25;
end;
if TopLeftXY[2] <= 0 then
TopLeftXY[2] := (HardVars.Depth - Fulldepth) div 2 +1;
if TopLeftXY[2] + Fulldepth - 1 <= HardVars.Depth then
begin
if BoxType > 4 then {shadow}
Y2 := TopleftXY[2] + (Fulldepth) - 2
else
Y2 := TopleftXY[2] + pred(Fulldepth);
end else
begin
if BoxType > 4 then {shadow}
Y2 := pred(HardVars.Depth)
else
Y2 := HardVars.Depth;
TopLeftXY[2] := HardVars.Depth - Fulldepth {+ 1}; {WZ}
end;
end;
end; { DetermineYDimensions }
begin { GetDimensions }
ValidatePrefix;
AddPrefix;
FindLongestTopic;
DetermineMaxWidth;
ValidatePicksPerLine;
AdjustTextWidth(TextWidth);
DetermineXDimensions;
DetermineYDimensions;
end; { GetDimensions }
procedure WriteText(Item:integer;Highlight:boolean);
{}
var X,Y,A: integer;
begin
with MenuDEf do
begin
A := Item mod PicksPerLine;
Y := Item div PicksPerLine +TopleftXY[2] + ord(A <> 0);
Y := Y + Headinglines - ord(Boxtype = 0);
if A = 0 then A := PicksPerLine; {A is now the no of picks from left}
X := (A - 1)*(TextWidth + 1)+Margins+
TopleftXY[1]+1 + ord(BoxType > 0); {title width + 1 for a space}
if Highlight then
begin
WriteHi(X,Y,colors[1],colors[2],Topic[item]);
WriteAT(pred(X),Y,colors[1],MenuVars.MenuLeft); {write arrow head}
WriteAT(X+TextWidth,Y,colors[1],MenuVars.MenuRight); {write arrow head}
end else
begin
if Topic[item] <> '' then
begin
if Topic[item][1] = '!' then {inactive}
WriteAt(pred(X),Y,Tint[MenuOff],' '+copy(Topic[item],2,255)+' ')
else
begin
WriteHi(pred(X),Y,colors[3],colors[4],' '+Topic[item]+' '); {remove arrow head}
if AddPrefix = 4 then {highlight the capital letter}
WriteAt(pred(X)+FirstCapitalPos(Topic[Item]),Y,
colors[3],
FirstCapital(Topic[Item]));
end;
end;
end;
end;
end; { WriteText }
procedure CreateMenu;
{}
var I: integer;
begin
with MenuDef do
begin
if Window then
begin
if BoxType <> 5 then
MkWin(TopleftXY[1],TopLeftXY[2],X2,Y2,colors[4],boxtype)
else
MkWin(TopleftXY[1],TopLeftXY[2],X2,Y2,colors[4],0);
end
else
begin
ClearText(TopleftXY[1],TopLeftXY[2],X2,Y2,colors[4]);
if (BoxType in [5..9]) and (TopleftXY[1] > 1) and not Window then
DrawShadow(TopLeftXY[1],TopLeftXY[2],X2,Y2);
end;
case Boxtype of
1..4: Box(TopLeftXY[1],TopLeftXY[2],X2,Y2,colors[5],Boxtype);
5 : begin
WriteAT(TopleftXY[1],TopleftXY[2],colors[5],
replicate(succ(X2 - TopleftXY[1]),chr(223)));
WriteAT(TopleftXY[1],TopleftXY[2]+pred(HeadingLines),colors[5],
replicate(succ(X2 - TopleftXY[1]),chr(196)));
end;
6..9: Box(TopLeftXY[1],TopLeftXY[2],X2,Y2,colors[5],Boxtype-5);
end; {case}
if length(Heading1) > 0 then
WriteBetween(TopleftXY[1],X2,
TopLeftXY[2]+ord(BoxType > 0),
colors[3],Heading1);
if length(Heading2) > 0 then
WriteBetween(TopleftXY[1],X2,
TopLeftXY[2]+ord(BoxType > 0)+ord(HeadingLines <> 2),
colors[3],Heading2);
for I := 1 to TotalPicks do
WriteText(I,false);
WriteText(Choice,True); {Highlight Default}
end; {do}
end; {CreateMenu}
function TargetPick(X,Y:integer): byte;
{Returns the value of the menu pick}
var Pick,Col: byte;
begin
TargetPick := 0;
with MenuDef do
if (X >= TopLeftXY[1])
and (Y >= TopLeftXY[2])
and (X <= X2)
and (Y <= Y2) then
begin
dec(X,TopLeftXY[1]+ord(BoxType > 0));
dec(Y,TopLeftXY[2]+Headinglines+ord(Boxtype = 0));
Col := succ(X div (TextWidth + succ(Margins)));
if (Y > 0) and (Col > 0) and (Col <= PicksPerLine) then
begin
Pick := pred(Y)*PicksPerLine + Col;
if (Pick <= TotalPicks)
and (Topic[Pick] <> '')
and (Topic[Pick][1] <> '-')then
TargetPick := Pick;
end;
end;
end; { TargetPick }
procedure ProcessMouse;
{}
var MouseChoice: byte;
begin
with KeyVars do
MouseChoice := TargetPick(LastX,LastY);
if not (MouseChoice in [0,Choice])then
begin
WriteText(Choice,false);
Choice := MouseChoice;
WriteText(Choice,true);
end;
MouseRelease;
end; { ProcessMouse }
procedure ProcessKeystrokes;
{}
var Found,
Selected: Boolean;
WKey: word;
Oldchoice:integer;
I:integer;
P,ScanTop,ScanBot,Cx,Cy: byte;
begin
Selected := false;
Found := false;
CursorFind(Cx,Cy,ScanTop,ScanBot);
CursorOff;
with MenuDef do
begin
repeat
Oldchoice := Choice;
GetInput;
WKey := KeyVars.LastKey;
Hook(WKey,Choice,Ecode); {call the user hook}
case Wkey of
336: begin {Cursor Down}
Writetext(Choice,false);
repeat
inc(Choice,PicksPerLine);
if Choice > TotalPicks then
Choice := (Choice mod PicksPerLine) + 1;
until ((Topic[Choice] <> '') and (Topic[Choice][1] <> '-'))
or (Choice = OldChoice);
WriteText(Choice,true);
end;
328: begin {cursor up}
WriteText(Choice,false);
repeat
dec(Choice,PicksPerLine);
if Choice < 1 then
begin
Choice := Choice + PicksPerline;
Choice := ((TotalPicks div PicksPerLine)*PicksPerLine)
- PicksPerLine + 1 + Choice - 2;
if Choice + PicksPerLine <= TotalPicks then
inc(Choice,PicksPerLine); {phew!}
end;
until ((Topic[Choice] <> '') and (Topic[Choice][1] <> '-'))
or (Choice = OldChoice);
WriteText(Choice,true);
end;
331: begin {cursor left}
WriteText(Choice,False);
repeat
dec(Choice);
if Choice = 0 then
Choice := TotalPicks;
until ((Topic[Choice] <> '') and (Topic[Choice][1] <> '-'))
or (Choice = OldChoice);
WriteText(Choice,true);
end;
32,333: begin
WriteText(Choice,false);
repeat
Choice := succ(Choice);
if Choice > TotalPicks then
Choice := 1;
until ((Topic[Choice] <> '') and (Topic[Choice][1] <> '-'))
or (Choice = OldChoice);
WriteText(Choice,true);
end;
327: begin {home key}
WriteText(Choice,false);
Choice := 1;
while (Choice <> OldChoice)
and (Choice <= TotalPicks)
and ((Topic[Choice] <> '') and (Topic[Choice][1] <> '-')) do
inc(Choice);
WriteText(Choice,true);
end;
335: begin {end key}
WriteText(Choice,false);
Choice := TotalPicks;
while (Choice <> OldChoice)
and (Choice > 1)
and ((Topic[Choice] <> '') and (Topic[Choice][1] <> '-')) do
dec(Choice);
WriteText(Choice,true);
end;
13: begin {enter key}
Selected := true;
Errorcode := 0;
end;
0: begin
Selected := true;
ErrorCode := Ecode;
end;
27: if AllowEsc then {Esc}
begin
Selected := true;
ErrorCode := 1;
end else
begin
WriteText(Choice,false);
Choice := TotalPicks;
WriteText(Choice,true);
end;
315..324: if Addprefix = 3 then {F1 to F10}
begin
if (TotalPicks >= Wkey - 314) and ((Topic[Wkey - 314] <> '') and (Topic[Wkey - 314][1] <> '-')) then
begin
WriteText(Choice,false);
Choice := Wkey - 314;
WriteText(Choice,true);
Selected := true;
Errorcode := 0;
end;
end;
49..57: if (AddPrefix in [1,3]) then {Number or Function Prefix} {4.02}
begin
if (TotalPicks >= WKey - 48)
and ((Topic[Wkey - 48] <> '') and (Topic[Wkey - 48][1] <> '-')) then
begin
WriteText(Choice,false);
Choice := Wkey-48;
WriteText(Choice,true);
Selected := true;
ErrorCode := 0;
end;
end;
97..122,
65..90: if AddPrefix = 2 then
begin
P := pos(upcase(char(WKey)),Alphabet);
if (P in [1..TotalPicks])
and ((Topic[P] <> '') and (Topic[P][1] <> '-')) then
begin
WriteText(Choice,false);
Choice := P;
WriteText(Choice,true);
Selected := true;
Errorcode := 0;
end;
end else
begin
if AddPrefix = 4 then
begin
Found := false;
I := Choice;
repeat
if (FirstCapital(Topic[I]) = upcase(char(Wkey)))
and (Topic[I] <> '')
and (Topic[I][1] <> '-') then
begin
Found := true;
WriteText(Choice,false);
Choice := I;
WriteText(Choice,true);
Selected := true;
Errorcode := 0;
end else
if I = TotalPicks then
I := 1
else
Inc(I);
until Found or (I = Choice);
end;
end;
500: if KeyVars.MouseVisible then
ProcessMouse;
540: if KeyVars.MouseVisible then
begin
with KeyVars do
I := TargetPick(LastX,LastY);
if I <> 0 then
begin
if I <> Choice then
begin
WriteText(Choice,false);
Choice := I;
WriteText(Choice,true);
end;
Selected := true;
Errorcode := 0;
end;
MouseRelease;
end;
end; {case}
Ecode := 0;
HindHook(Choice,Ecode); {call the user hind hook}
if ECode <> 0 then
begin
ErrorCode := Ecode;
Selected := true;
end;
until Selected;
CursorSize(ScanTop,ScanBot);
end; {do}
end; { ProcessKeystrokes }
begin
GetDimensions;
if (Choice < 1) or (Choice > Menudef.TotalPicks) then
Choice := 1;
CreateMenu;
Ecode := 0;
MenuDef.HindHook(Choice,Ecode); {call the user hind hook}
if ECode <> 0 then
ErrorCode := Ecode
else
ProcessKeystrokes;
if Window then
RmWin;
end; {DisplayMenu}
{************************************}
{** P U L L D O W N M E N U S **}
{************************************}
procedure InitBar(var P: Bar);
{}
begin
with P do
begin
ActiveChain := nil;
FirstItem := nil;
HKs := nil;
MainCount := 0;
TopX := 1;
TopY := 1;
DescX1 := MenuVars.MsgX1;
DescX2 := MenuVars.MsgX2;
DescY := MenuVars.MsgY;
Style := MenuVars.PullStyle;
ActiveItem := 1;
EraseFullLine := true;
HelpHook := NoPullHook;
HelpActive := false;
HelpKey := MenuVars.Helpkey;
HindHook := NoPullHook;
end;
end; {InitBar}
procedure InitPopUp(var M:PopUp);
{}
begin
with M do
begin
FirstItem := nil;
ActiveItem := 1;
Width := 0;
ItemCount := 0;
end;
end; { InitPopUp }
procedure AssignMenuHelpHook(var M: Bar; Proc:PullHook);
{}
begin
M.HelpHook := Proc;
M.HelpActive := @Proc <> @NoPullHook;
end; { AssignMenuHelpHook }
procedure AssignMenuHindHook(var M: Bar; Proc:PullHook);
{}
begin
M.HindHook := Proc;
end; { AssignMenuHindHook }
procedure RemoveMenuHelpHook(var M: Bar);
{}
begin
AssignMenuHelpHook(M,NoPullHook);
end; { RemoveMenuHelpHook }
procedure RemoveMenuHindHook(var M: Bar);
{}
begin
AssignMenuHindHook(M,NoPullHook);
end; { RemoveMenuHindHook }
{*************************}
{** Pointer Functions **}
{*************************}
function MainItemPtr(var M: Bar; Num:byte): PullItemPtr;
{}
var
MIP:PullItemPtr;
I: integer;
begin
if Num > M.MainCount then
MainItemPtr := nil
else
begin
MIP := M.FirstItem;
for I := 2 to Num do
MIP := MIP^.NextPtr;
MainItemPtr := MIP;
end;
end; { MainItemPtr }
function PopUpItemPtr(var P: PopUp; Num:byte): PullItemPtr;
{}
var
MIP:PullItemPtr;
I: integer;
begin
if Num > P.ItemCount then
PopUpItemPtr := nil
else
begin
MIP := P.FirstItem;
for I := 2 to Num do
MIP := MIP^.NextPtr;
PopUpItemPtr := MIP;
end;
end; { PopUpItemPtr }
function PreviousPopUpPtr(var M: Bar; Active: PopUpPtr):PopUpPtr;
var
CP: ChainItemPtr;
begin
CP := M.ActiveChain;
while (CP <> nil)
and (CP^.NextPtr <> nil)
and (CP^.NextPtr^.PopUp <> Active) do
CP := CP^.NextPtr;
if ((CP = nil) or (CP^.NextPtr = nil)) then
PreviousPopUpPtr := nil
else
PreviousPopUpPtr := CP^.PopUp;
end; {PreviousPopUpPtr}
function ItemIDPtr(IP:PullItemPtr; ID:integer):PullItemPtr;
{}
begin
while (IP <> nil) and (IP^.ID <> ID) do
IP := IP^.NextPtr;
ItemIDPtr := IP;
end; { ItemIDPtr }
{*********************}
{** Display procs **}
{*********************}
procedure DisplayHelpStr(var M:Bar; Hi:boolean);
{}
var A1,A2: byte;
begin
if M.HelpActive then
begin
if Hi then
begin
A1 := Tint[PullHiHot];
A2 := Tint[PullHi];
end
else
begin
A1 := Tint[PullNormHot];
A2 := Tint[PullNorm];
end;
WriteHi(M.DescX1,M.DescY,A1,A2,MenuVars.HelpStr);
end;
end; { DisplayHelpStr }
procedure DisplayMainItem(var M:Bar; MP: PullItemPtr; Y:byte; Hi:boolean);
{}
var AHot,ANorm: byte;
begin
if not MP^.Active then
begin
Ahot := Tint[PullOff];
ANorm := Tint[PullOff];
end
else if Hi then
begin
Ahot := Tint[PullHiHot];
ANorm := Tint[PullHi];
end
else
begin
Ahot := Tint[PullNormHot];
ANorm := Tint[PullNorm];
end;
with MP^ do
if Topic^ <> '' then
WriteHi(XPackedWord(Status).X1,Y,AHot,ANorm,' '+Topic^+' ');
if Hi then
begin
if M.TopY <> M.DescY then
begin
M.HelpMargin := 0;
if not M.MsgDisplayed then {first message}
begin
ClearText(M.DescX1,M.DescY,M.DescX2,M.DescY, Tint[PullMsg]);
DisplayHelpStr(M,false);
if (MenuVars.HelpStr <> '') and M.HelpActive and (M.Style <> 4) then
begin
M.HelpMargin := length(Strip('A',HiMarker,MenuVars.HelpStr))+2;
WritePlain(pred(M.DescX1 + pred(M.HelpMargin)),M.DescY,'│');
end
else
end
else
ClearText(M.DescX1+M.HelpMargin,M.DescY,M.DescX2,M.DescY, Tint[PullMsg]);
if MP^.LongDesc^ <> '' then
WriteHiX2(M.DescX1+M.HelpMargin,M.DescX2,M.DescY,Tint[PullMsgHot],Tint[PullMsg],MP^.LongDesc^);
end;
end;
end; { DisplayMainItem }
function ExpandedItem(var Item: string; Width:byte):string;
{INTERNAL - expands the tab stops (if necessary) and returns the padded string}
var P,C: byte;
begin
P := pos(MenuVars.TabChar,Item);
C := CharCount(HiMarker,Item);
if P = 0 then
ExpandedItem := padleft(Item,width+C,' ')
else
ExpandedItem := copy(Item,1,pred(P))+replicate(succ(width)+C-length(Item),' ')+copy(Item,succ(P),255);
end; { ExpandedItem }
procedure DisplayPopUpItem(var M:Bar; var P:PopUp;ItemNum:byte;Selected:boolean; Style:byte);
{}
var
Y,A1,A2:byte;
IP: PullItemPtr;
Txt: string[80];
begin
with P do
begin
IP := PopUpItemPtr(P,ItemNum);
if IP <> nil then
begin
Y := Y1 + ItemNum;
if IP^.Topic^ = MenuVars.Separator then
begin
A1 := Tint[PullBorder1];
case style of
1: begin
WriteAt(succ(X1),Y,Tint[PullBorder1],'├');
WriteAt(pred(X2),Y,Tint[PullBorder1],'┤');
WriteAT(X1+2,Y,Tint[PullBorder1],replicate(X2-X1-3,'─'));
end;
2,3,4: begin
WriteAT(X1+2,Y,Tint[PullBorder2],replicate(X2-X1-3,'─'));
end;
end; {case}
end
else
begin
if Selected then
begin
if not IP^.Active then
begin
A1 := Cattr(FAttr(Tint[PullOff]),BAttr(Tint[PullHi]));
A2 := A1;
end
else
begin
A1 := Tint[PullHiHot];
A2 := Tint[PullHi];
end;
end
else if not IP^.Active then
begin
A1 := Tint[PullOff];
A2 := Tint[PullOff];
end
else
begin
A1 := Tint[PullNormHot];
A2 := Tint[PullNorm];
end;
Txt := ExpandedItem(IP^.Topic^,X2-X1-5);
if IP^.ChildPopUp <> nil then
Txt[length(txt)] := '';
with MenuVars do
if Selected then
WriteHi(X1+2,Y,A1,A2,PullLeft+Txt+PullRight)
else
WriteHi(X1+2,Y,A1,A2,' '+Txt+' ');
end;
end;
end;
if Selected then
begin
ClearText(M.DescX1+M.HelpMargin,M.DescY,M.DescX2,M.DescY, Tint[PullMsg]);
if IP^.LongDesc^ <> '' then
WriteHiX2(M.DescX1+M.HelpMargin,M.DescX2,M.DescY,Tint[PullMsgHot],Tint[PullMsg],IP^.LongDesc^);
end;
end; { DisplayPopUpItem }
procedure DrawBar(var M: Bar);
{Draws the menu bar and verifies the X location of individual menu items}
var
MP: PullItemPtr;
StartX : byte;
begin
if (M.Style > 4) or (M.Style < 1) then
M.Style := MenuVars.PullStyle;
with M do
if (MainCount > 0) and (FirstItem <> nil) then
begin
MP := FirstItem;
StartX := succ(TopX);
case Style of
1,2,3: begin
MainY := TopY;
if EraseFullLine then
ClearLine(TopY,Tint[PullNorm]);
end;
4: begin
StartX := TopX + 2;
MainY := succ(TopY);
if FastVars.CustomCharsActive then
WriteAt(TopX,TopY,Tint[PullBorder2],chr(206)+chr(207))
else
WriteAt(TopX,TopY,Tint[PullBorder2],' ─ ');
end;
end; {case}
while MP <> nil do
begin
with MP^ do
with XPackedWord(Status) do
begin
X1 := StartX;
X2 := X1 + succ(length(Strip('A',HiMarker,Topic^)));
DisplayMainItem(M,MP,MainY,false);
StartX := succ(X2);
end;
MP := MP^.NextPtr;
end;
MainX2 := pred(StartX);
end;
end; {DrawBar}
function WidestTopic(P:PopUpPtr): byte;
{}
var
TopicPtr: PullItemPtr;
W,Widest: byte;
begin
Widest := 0;
TopicPtr := P^.FirstItem;
while TopicPtr <> nil do
begin
W := length(Strip('A',HiMarker,TopicPtr^.Topic^))
+ ord(TopicPtr^.ChildPopUp <> nil);
if pos(MenuVars.TabChar,TopicPtr^.Topic^) <> 0 then
inc(W,2);
if Widest < W then
Widest := W;
TopicPtr := TopicPtr^.NextPtr;
end;
WidestTopic := Widest;
end; { WidestTopic }
procedure DrawPopUp(var M: Bar; P:PopUpPtr; ActiveOn: boolean);
{INTERNAL}
var
I: integer;
CP: ChainItemPtr;
begin
with M do
begin
MenuDown := true;
{determine the X and Y coords}
with P^ do
begin
if ActiveChain = nil then {no pop-ups visible}
begin
X1 := pred(XPackedWord(MainItemPtr(M,M.ActiveItem)^.Status).X1);
Y1 := succ(M.MainY);
end
else
begin
CP := ActiveChain;
while CP^.NextPtr <> nil do
CP := CP^.NextPtr;
X1 := CP^.PopUp^.X1 + 2;
Y1 := CP^.PopUp^.Y1 + CP^.PopUp^.ActiveItem + 1;
end;
if (Width <> 0) and (Width > 8) then
X2 := pred(X1 + Width)
else {find widest topic}
X2 := X1 + WidestTopic(P) + 5;
Y2 := Y1 + succ(ItemCount);
case M.Style of
1: MkPopUpWin(X1,Y1,X2,Y2,Tint[PullBorder1],Tint[PullBorder1],1);
2: MkPopUpWin(X1,Y1,X2,Y2,Tint[PullBorder1],Tint[PullBorder2],1);
3,4: MkPopUpWin(X1,Y1,X2,Y2,Tint[PullBorder1],Tint[PullBorder2],3);
end;
for I := 1 to ItemCount do
DisplayPopUpItem(M,P^,I,ActiveOn and (I=ActiveItem),M.Style);
end;
{now update the chain}
if ActiveChain = nil then
begin
getmem(ActiveChain,sizeof(ActiveChain^));
CP := ActiveChain;
end
else
begin
CP := ActiveChain;
while CP^.NextPtr <> nil do
CP := CP^.NextPtr;
getmem(CP^.NextPtr,sizeof(CP^));
CP := CP^.NextPtr;
end;
CP^.NextPtr := nil;
CP^.PopUp := P;
end;
end; { DrawPopUp }
{*********************************}
{** Main Menu Item Management **}
{*********************************}
procedure PullItemDispose(IP: PullItemPtr;T,L:boolean);
{INTERNAL}
begin
if IP <> nil then
with IP^ do
begin
if T and (Topic <> nil) then
begin
freemem(Topic,ord(Topic^[0]));
Topic := nil;
end;
if L and (LongDesc <> nil) then
begin
freemem(LongDesc,ord(LongDesc^[0]));
LongDesc := nil;
end;
end;
end; { PullItemDispose }
procedure PullItemUpdate(IP: PullItemPtr;Item,Desc:string;HK,AHK:word;ID:integer; PopUp:pointer);
{INTERNAL}
var L: byte;
begin
if IP <> nil then
with IP^ do
begin
if Item[1] = MenuVars.InactiveChar then
begin
Active := false;
delete(Item,1,1);
end
else
Active := true;
PullItemDispose(IP,Item <> NoChange,Desc <> NoChange);
if Item <> NoChange then
begin
L := succ(length(Item));
getmem(Topic,L);
move(Item,Topic^,L);
end;
if (Desc <> NoChange) and (Desc <> '') then
begin
L := succ(length(Desc));
getmem(LongDesc,L);
move(Desc,LongDesc^,L);
end;
ChildPopUp := PopUp;
end;
IP^.ID := ID;
IP^.HK := HK;
IP^.AHK := AHK;
end; { PullItemUpdate }
function EngineAddItem(var FirstItem:PullItemPtr):PullItemPtr;
{INTERNAL}
var NewItem: PullItemPtr;
begin
if FirstItem = nil then
begin
getmem(FirstItem,sizeof(FirstItem^));
NewItem := FirstItem;
end
else
begin
NewItem := FirstItem;
while NewItem^.NextPtr <> nil do
NewItem := NewItem^.NextPtr;
getmem(NewItem^.NextPtr,sizeof(NewItem^));
NewItem := NewItem^.NextPtr;
end;
with NewItem^ do
begin
NextPtr := nil;
Topic := nil;
LongDesc := nil;
end;
EngineAddItem := NewItem;
end; { EngineAddItem }
procedure BarAddItem(var M: Bar; Item:string; ID:integer; HK,AltHK:word; Desc:string; PopUp:pointer);
{Called by user when adding a new item to the top menu}
var NewItem: PullItemPtr;
begin
if (Item <> '') and (Item <> MenuVars.InActiveChar) then
begin
if GoldMemAvail < sizeof(M.FirstItem^) + 2 + length(Item) + length(Desc) then
MenuSetError(1002)
else with M do
begin
inc(MainCount);
NewItem := EngineAddItem(FirstItem);
PullItemUpdate(NewItem,Item,Desc,HK,AltHK,ID,PopUp);
end;
end;
end; { BarAddItem }
procedure BarSetActive(var M: Bar; ID:integer; On: boolean);
{}
var IP:PullItemPtr;
begin
IP := ItemIDPtr(M.FirstItem,ID);
if IP <> nil then
IP^.Active := On
else
MenuSetError(1003);
end; { BarSetActive }
procedure BarChangeItem(var M: Bar; Item:string; ID,NewID:integer; HK,AltHK:word; Desc:string; PopUp:pointer);
{}
var IP:PullItemPtr;
begin
IP := ItemIDPtr(M.FirstItem,ID);
if IP <> nil then
PullItemUpdate(IP,Item,Desc,HK,AltHK,NewID,PopUp)
else
MenuSetError(1003);
end; { BarChangeItem }
procedure BarChangeText(var M: Bar; ID:integer; Item,Desc:string);
{}
var IP:PullItemPtr;
begin
IP := ItemIDPtr(M.FirstItem,ID);
if IP <> nil then
PullItemUpdate(IP,Item,Desc,IP^.HK,IP^.AHK,IP^.ID,IP^.ChildPopUp)
else
MenuSetError(1003);
end; { BarChangeText }
procedure EngineDelItem(FirstPtr:PullItemPtr; ID:integer);
{}
var IP,PrevPtr:PullItemPtr;
begin
IP := ItemIDPtr(FirstPtr,ID);
if IP <> nil then
begin
if IP = FirstPtr then
FirstPtr := IP^.NextPtr
else
begin
PrevPtr := FirstPtr;
while PrevPtr^.NextPtr <> IP do
PrevPtr := PrevPtr^.NextPtr;
PrevPtr^.NextPtr := IP^.NextPtr;
end;
PullItemDispose(IP,true,true);
freemem(IP,sizeof(IP^));
end
else
MenuSetError(1003);
end; { EngineDelItem }
procedure BarDelItem(var M: Bar; ID:integer);
{}
begin
EngineDelItem(M.FirstItem,ID);
end; { BarDelItem }
{**********************************}
{** Popup Menu Item Management **}
{**********************************}
procedure PopupAddItem(var P:PopUp;Item:string; ID:integer; HK:word; Desc:string; ChildMenu:pointer);
{}
var NewItem: PullItemPtr;
begin
if (Item <> '') and (Item <> MenuVars.InActiveChar) then
begin
if GoldMemAvail < sizeof(P.FirstItem^) + 2 + length(Item) + length(Desc)then
MenuSetError(1002)
else with P do
begin
inc(ItemCount);
NewItem := EngineAddItem(FirstItem);
PullItemUpdate(NewItem,Item,Desc,HK,0,ID,ChildMenu);
end;
end;
end; { PopUpAddItem }
procedure PopUpSetActive(var P:PopUp; ID:integer; On: boolean);
{}
var IP:PullItemPtr;
begin
IP := ItemIDPtr(P.FirstItem,ID);
if IP <> nil then
IP^.Active := On
else
MenuSetError(1003);
end; { PopUpSetActive }
procedure PopupChangeItem(var P:PopUp;Item:string; ID,NewID:integer; HK:word; Desc:string; ChildMenu:pointer);
{}
var IP:PullItemPtr;
begin
IP := ItemIDPtr(P.FirstItem,ID);
if IP <> nil then
PullItemUpdate(IP,Item,Desc,HK,0,NewID,ChildMenu)
else
MenuSetError(1003);
end; { PopUpChangeItem }
procedure PopUpChangeText(var P:PopUp; ID:integer; Item,Desc:string);
{}
var IP:PullItemPtr;
begin
IP := ItemIDPtr(P.FirstItem,ID);
if IP <> nil then
PullItemUpdate(IP,Item,Desc,IP^.HK,0,IP^.ID,IP^.ChildPopUp)
else
MenuSetError(1003);
end; { PopUpChangeText }
procedure PopUpDelItem(var P:PopUp; ID:integer);
{}
begin
EngineDelItem(P.FirstItem,ID);
end; { PopUpDelItem }
procedure RemoveTopPopUp(var M: Bar);
{INTERNAL - only called when at least two pop-ups}
var CP: ChainItemPtr;
begin
CP := M.ActiveChain;
while CP^.NextPtr^.NextPtr <> nil do
CP := CP^.NextPtr;
freemem(CP^.NextPtr,sizeof(CP^));
CP^.NextPtr := nil;
RmWin;
end; { RemoveTopPopUp }
procedure RemoveAllPopUps(var M: Bar);
{INTERNAL}
var
I,Counter: integer;
Temp1,Temp2: ChainItemPtr;
begin
with M do
begin
MenuDown := false;
Temp1 := ActiveChain;
Counter := 0;
while Temp1 <> nil do
begin
Temp2 := Temp1^.NextPtr;
inc(Counter);
freemem(Temp1,sizeof(temp1^));
Temp1 := Temp2;
end;
for I := 1 to Counter do
RmWin;
ActiveChain := nil;
end;
end; { RemoveAllPopUps }
{**************************}
{** HK Management **}
{**************************}
procedure BarAddHK(var P: Bar; K:word; HotID:integer);
{}
var NewKey: HKPtr;
begin
if GoldMemAvail < sizeof(p.HKs^) then
MenuSetError(1002)
else with P do
begin
if HKs = nil then
begin
getmem(HKs,sizeof(HKs^));
NewKey := HKs;
end
else
begin
NewKey := HKs;
while NewKey^.NextPtr <> nil do
NewKey := NewKey^.NextPtr;
getmem(NewKey^.NextPtr,sizeof(NewKey^));
NewKey := NewKey^.NextPtr;
end;
with NewKey^ do
begin
HK := K;
ID := HotID;
NextPtr := nil;
end;
end;
end; { BarAddHK }
procedure BarDelHK(var P: Bar; K:word);
{}
var
Temp, NewKey: HKPtr;
begin
if P.HKs^.HK = K then {first Item matches}
begin
Temp := P.HKs;
P.HKs := P.HKs^.NextPtr;
freemem(Temp,sizeof(Temp^));
end
else
begin
NewKey := P.HKs;
while (NewKey^.NextPtr <> nil) and (Newkey^.NextPtr^.HK <> K) do
NewKey := NewKey^.NextPtr;
if NewKey^.NextPtr <> nil then {found it}
begin
Temp := NewKey^.NextPtr;
NewKey^.NextPtr := Temp^.NextPtr;
freemem(Temp,sizeof(Temp^));
end;
end;
end; { BarDelHK }
procedure DestroyAllHKs(var M: Bar);
{INTERNAL}
var
Temp1, Temp2: HKPtr;
S:word;
begin
Temp1 := M.HKs;
S := sizeof(Temp1^);
while Temp1 <> nil do
begin
Temp2 := Temp1^.NextPtr;
freemem(Temp1,S);
Temp1 := Temp2;
end;
M.HKs := nil;
end; { DestroyAllHKs }
{****************************}
{** Menu Memory Disposal **}
{****************************}
procedure DestroyItemChain(FirstItem:PullItemPtr);
{}
var
Ptr1,Ptr2: PullItemPtr;
begin
Ptr1 := FirstItem;
while Ptr1 <> nil do
begin
Ptr2 := Ptr1^.NextPtr;
with Ptr1^ do
begin
if Topic <> nil then
freemem(Topic, succ(ord(Topic^[0])));
if LongDesc <> nil then
freemem(LongDesc, succ(ord(LongDesc^[0])));
end;
freemem(Ptr1,sizeof(Ptr1^));
Ptr1 := Ptr2;
end;
end; { DestroyItemChain }
procedure DestroyPopUp(P:PopUp);
{}
begin
DestroyItemChain(P.FirstItem);
P.FirstItem := nil;
end; { DestroyPopUp }
procedure DestroyBar(M:Bar);
{}
begin
DestroyAllHKs(M);
DestroyItemChain(M.FirstItem);
M.FirstItem := nil;
end; { DestroyBar }
{********************************}
{** Pull Keyboard Management **}
{********************************}
function AltHotkeyTopicItem(var P: Bar; K:word): integer;
{Returns the number (not ID) of the topic whose hot key was pressed, or
0 if not a valid Bar hotkey}
var
MP: PullItemPtr;
Counter: integer;
begin
MP := P.FirstItem;
Counter := 1;
while (MP <> nil) and not ((MP^.AHK = K) and (MP^.Active)) do
begin
MP := MP^.NextPtr;
inc(Counter);
end;
if MP = nil then
AltHotkeyTopicItem := 0
else
AltHotkeyTopicItem := Counter;
end; { HotkeyTopicItem }
function HotkeyTopicItem(IP:PullItemPtr; Hotkey:word): byte;
{Returns the byte of the topic whose hotkey was pressed or 0 for not found}
var Counter: integer;
begin
HotKey := CapitalWord(Hotkey);
Counter := 1;
while (iP <> nil) and not ((IP^.HK = Hotkey) and (IP^.Active)) do
begin
IP := IP^.NextPtr;
inc(Counter);
end;
if IP = nil then
HotkeyTopicItem := 0
else
HotkeyTopicItem := Counter;
end; { HotkeyTopicItem }
function HKTopicID(var P: Bar; K:word): integer;
{}
var KP: HKPtr;
begin
HKTopicID := 0;
KP := P.HKs;
while (KP <> nil) and (KP^.HK <> K) do
KP := KP^.NextPtr;
if KP <> nil then
HKTopicID := KP^.ID;
end; { HKTopicID }
function IsPullKey(var P: Bar; K:word; X,Y:byte):boolean;
{}
begin
with P do
if (K = MenuVars.ActivateKey)
or (
(K = 500) and (Y = P.MainY) and (X >= TopX) and (X <= MainX2)
) then
IsPullkey := true
else if HKTopicID(P,K) <> 0 then
IsPullKey := true
else
IsPullKey := AltHotkeyTopicItem(P,K) <> 0;
end; {IsPullKey}
function BarProcessKey(var M: Bar; K:word; X,Y:byte; var ID:integer): boolean;
{Returns true if user made a selection or escaped}
var
TempItem: byte;
CP: PopUpPtr;
IP: PullItemPtr;
procedure SelectTopic;
{}
begin
CP := MainItemPtr(M,M.ActiveItem)^.ChildPopUp;
if CP <> nil then
DrawPopUp(M,CP,true)
else
begin
BarProcessKey := true;
ID := MainItemPtr(M,M.ActiveItem)^.ID;
end;
end; { SelectTopic }
begin
BarProcessKey := false;
with M do
case K of
500: ; {mouse down}
13: begin {mouse enter}
if MainItemPtr(M,M.ActiveItem)^.Active then
SelectTopic;
end;
336: begin
CP := MainItemPtr(M,M.ActiveItem)^.ChildPopUp;
if CP <> nil then
DrawPopUp(M,CP,true);
end;
331: if MainCount > 1 then {left}
begin
DisplayMainItem(M,MainItemPtr(M,ActiveItem),MainY,false);
TempItem := ActiveItem;
repeat
if ActiveItem = 1 then
ActiveItem := MainCount
else
dec(ActiveItem);
until MainItemPtr(M,ActiveItem)^.Active or (TempItem = ActiveItem);
IP := MainItemPtr(M,ActiveItem);
DisplayMainItem(M,IP,MainY,true);
ID := ActiveItem;
if MenuDown and (IP^.ChildPopUp <> nil) then
DrawPopUp(M,IP^.ChildPopUp,true);
end;
333: begin {right}
DisplayMainItem(M,MainItemPtr(M,ActiveItem),MainY,false);
TempItem := ActiveItem;
repeat
if ActiveItem = MainCount then
ActiveItem := 1
else
inc(ActiveItem);
until MainItemPtr(M,ActiveItem)^.Active or (TempItem = ActiveItem);
IP := MainItemPtr(M,ActiveItem);
DisplayMainItem(M,IP,MainY,true);
ID := ActiveItem;
if MenuDown and (IP^.ChildPopUp <> nil) then
DrawPopUp(M,IP^.ChildPopUp,true);
end;
27: begin
BarProcessKey := true;
ID := 0;
end;
32..255: begin {user pressed a letter}
TempItem := HotkeyTopicItem(M.FirstItem,K);
if TempItem <> 0 then
begin
DisplayMainItem(M,MainItemPtr(M,ActiveItem),MainY,false);
ActiveItem := TempItem;
DisplayMainItem(M,MainItemPtr(M,ActiveItem),MainY,true);
ID := ActiveItem;
SelectTopic;
end;
end;
end;
end; { BarProcessKey }
function PopUpProcessKey(var M: Bar;CP: ChainItemPtr; K:word; X,Y:byte; var ID:integer): boolean;
{Returns true if user made a selection or escaped}
var
TempItem: byte;
IP: PullItemPtr;
procedure SelectTopic;
{}
begin
with CP^.PopUp^ do
begin
IP := PopUpItemPtr(CP^.PopUp^,ActiveItem);
if IP^.ChildPopUp <> nil then
DrawPopUp(M,IP^.ChildPopUp,true)
else
begin
PopUpProcessKey := true;
ID := IP^.ID;
end;
end;
end; { SelectTopic }
begin
PopUpProcessKey := false;
with CP^.PopUp^ do
case K of
13: begin {enter}
if PopUpItemPtr(CP^.PopUp^,ActiveItem)^.Active then
SelectTopic;
end;
331: begin
PopUpProcessKey := true;
ID := GPullLeft;
end;
333: begin
PopUpProcessKey := true;
ID := GPullRight;
end;
328: begin {up}
DisplayPopUpItem(M,CP^.PopUp^,ActiveItem,false,M.Style);
TempItem := ActiveItem;
repeat
if ActiveItem = 1 then
ActiveItem := ItemCount
else
dec(ActiveItem);
IP := PopUpItemPtr(CP^.PopUp^,ActiveItem);
until ( (IP^.Topic^ <> MenuVars.Separator) and IP^.Active)
or
(TempItem = ActiveItem);
DisplayPopUpItem(M,CP^.PopUp^,ActiveItem,true,M.Style);
ID :=ActiveItem;
end;
336: begin {down}
DisplayPopUpItem(M,CP^.PopUp^,ActiveItem,false,M.Style);
TempItem := ActiveItem;
repeat
if ActiveItem = ItemCount then
ActiveItem := 1
else
inc(ActiveItem);
IP := PopUpItemPtr(CP^.PopUp^,ActiveItem);
until ( (IP^.Topic^ <> MenuVars.Separator) and IP^.Active)
or
(TempItem = ActiveItem);
DisplayPopUpItem(M,CP^.PopUp^,ActiveItem,true,M.Style);
ID :=ActiveItem;
end;
27: begin
PopUpProcessKey := true;
ID := 0;
end;
32..255: begin {user pressed a letter}
TempItem := HotkeyTopicItem(CP^.PopUp^.FirstItem,K);
if TempItem <> 0 then
begin
DisplayPopUpItem(M,CP^.PopUp^,ActiveItem,false,M.Style);
ActiveItem := TempItem;
DisplayPopUpItem(M,CP^.PopUp^,ActiveItem,true,M.Style);
SelectTopic;
end;
end;
end; {case}
end; { PopUpProcessKey }
function ActiveMenuID(var M: Bar): integer;
{}
var
CP: ChainItemPtr;
begin
with M do
begin
if M.ActiveChain = nil then
ActiveMenuID := MainItemPtr(M,M.ActiveItem)^.ID
else
begin
CP := M.ActiveChain;
while CP^.NextPtr <> nil do
CP := CP^.NextPtr;
ActiveMenuID := PopUpItemPtr(CP^.PopUp^,CP^.PopUp^.ActiveItem)^.ID;
end;
end;
end; { ActiveMenuID }
function PullProcessMouse(var M: Bar; var ID:integer): boolean;
{Called when the user presses the mouse down}
var
L,Middle,R: boolean;
X,Y:byte;
P: PullItemPtr;
PopUpCount,
Counter: integer;
ActivePopUp: PopUpPtr;
PopUpItemHi: boolean;
CP: ChainItemPtr;
procedure MouseOnMainBar;
{}
begin
with M do
begin
P := FirstItem;
Counter := 1;
while (P <> nil)
and ( (X < XPackedWord(P^.Status).X1) or (X > XPackedWord(P^.Status).X2)) do
begin
P := P^.NextPtr;
inc(Counter);
end;
if (P <> nil)
and (
(Counter <> ActiveItem)
or
((P^.ChildPopUp <> nil) and (ActiveChain = nil))
)
and (P^.Active) then
begin
if ActiveChain <> nil then
RemoveAllPopUps(M);
DisplayMainItem(M,MainItemPtr(M,ActiveItem),MainY,false);
ActiveItem := Counter;
DisplayMainItem(M,P,MainY,true);
if (P^.ChildPopUp <> nil) then
begin
DrawPopUp(M,P^.ChildPopUp,false);
MenuDown := true;
end;
PopUpItemHi := false;
end;
end;
end; { MouseOnMainBar }
function WhichPopUp:PopUpPtr;
{}
var
P: PopUpPtr;
begin
WhichPopUp := nil;
CP := M.ActiveChain;
Counter := 1;
while (CP <> nil) and (CP^.NextPtr <> nil) do
begin
CP := CP^.NextPtr;
inc(Counter);
end;
PopUpCount := Counter;
if CP <> nil then
begin
P := CP^.PopUp;
while P <> nil do
begin
with P^ do
if (X >= X1) and (Y >= Y1) and (X <= X2) and (Y <= y2) then
begin
WhichPopUp := P;
exit;
end
else
begin
P := PreviousPopUpPtr(M,P);
dec(Counter);
end;
end; {while}
end;
end; { WhichPopUp }
procedure MouseOnPopUp;
{}
var
P: PopUpPtr;
ItemNum: byte;
I: integer;
begin
P := WhichPopUp;
if P <> nil then {mouse is over a PopUp}
with P^ do
begin
for I := succ(Counter) to PopUpCount do
RemoveTopPopUp(M);
if (X > succ(X1)) and (X < pred(X2)) and (Y > Y1) and (Y < Y2)
and (PopUpItemPtr(P^,Y-Y1)^.Topic^ <> MenuVars.Separator) then
begin
if (PopUpItemHi = false) or (ActiveItem <> Y-Y1) then
begin
if ActiveItem <> Y-Y1 then
begin
DisplayPopUpItem(M,P^,ActiveItem,false,M.Style);
ActiveItem := Y - Y1;
end;
DisplayPopUpItem(M,P^,ActiveItem,true,M.Style);
PopUpItemHi := true;
end;
end
else {on border}
begin
if PopUpItemHi then
begin
DisplayPopUpItem(M,P^,ActiveItem,false,M.Style);
PopUpItemHi := false;
end;
end;
end;
end; { MouseOnPopUp }
function MouseOverHelpText(X,Y:byte): boolean;
{}
begin
with M do
MouseOverHelpText := (Y = DescY)
and (X >= DescX1)
and (X <= pred(DescX1) + length(Strip('A',HiMarker,MenuVars.HelpStr)));
end; { MouseOverHelpText }
procedure MouseHelpAction;
{}
var
A1,A2: byte;
Hi: boolean;
begin
Hi := false;
repeat
MouseStatus(L,Middle,R,X,Y);
if MouseOverHelpText(X,Y) and not Hi then
begin
DisplayHelpStr(M,true);
Hi := true;
end else if Hi and not MouseOverHelpText(X,Y) then
begin
DisplayHelpStr(M,false);
Hi := false;
end;
until not L;
if MouseOverHelpText(X,Y) then
begin
SaveScreen(InternalScreen3);
M.HelpHook(ActiveMenuID(M));
RestoreScreen(InternalScreen3);
DisposeScreen(InternalScreen3);
end;
DisplayHelpStr(M,false);
MouseRelease;
PullProcessMouse := false;
end; { MouseHelpAction }
begin
PopUpItemHi := true;
with M do
begin
MouseStatus(L,Middle,R,X,Y);
if HelpActive and L and (M.Style <> 4) and MouseOverHelpText(X,Y) then
MouseHelpAction
else
begin
repeat
MouseStatus(L,Middle,R,X,Y);
if (Y = M.MainY) and (X >= TopX) and (X <= MainX2) then {on main bar}
MouseOnMainBar
else
begin
ActivePopUp := WhichPopUp;
if ActivePopUp <> nil then
MouseOnPopUp
else if PopUpItemHi then {need to turn off active pick}
begin
PopUpItemHi := false;
CP := ActiveChain;
while (CP <> nil) and (CP^.NextPtr <> nil) do
CP := CP^.NextPtr;
DisplayPopUpItem(M,CP^.PopUp^,CP^.PopUp^.ActiveItem,false,M.Style);
end;
end;
until not L;
MouseRelease;
if (Y = MainY) and (X >= TopX) and (X <= MainX2) then {on main bar}
begin
if ActiveChain <> nil then {Activate the Pop-Up}
begin
PullProcessMouse := false;
DisplayPopUpItem(M,ActiveChain^.PopUp^,ActiveChain^.PopUp^.ActiveItem,true,M.Style);
end
else
begin
PullProcessMouse := true;
if MainItemPtr(M,M.ActiveItem)^.Active then
ID := MainItemPtr(M,M.ActiveItem)^.ID
else
ID := 0;
end;
end
else {see if mouse on top popup}
begin
CP := ActiveChain;
while (CP <> nil) and (CP^.NextPtr <> nil) do
CP := CP^.NextPtr;
if (CP <> nil) and PopUpItemHi then {mouse is over a popup topic}
begin
P := PopUpItemPtr(CP^.PopUp^,CP^.PopUp^.ActiveItem);
if P^.Active then
begin
ID := P^.ID;
if P^.ChildPopUp <> nil then
begin
PullProcessMouse := false;
DrawPopUp(M,P^.ChildPopUp,true)
end
else
PullProcessMouse := true;
end
else
PullProcessMouse := false;
end
else {mouse released away from menu areas}
begin
PullProcessMouse := true;
ID := 0
end;
end;
end;
end;
end; { PullProcessMouse }
function PullProcessKey(var M: Bar; K:word; X,Y:byte; var ID:integer):boolean;
{Returns true if user made a selection or escaped}
var
CP: ChainItemPtr;
MIP:PullItemPtr;
Item: byte;
procedure SendtoPopup;
{}
begin
with M do
begin
if PopUpProcessKey(M,CP,K,X,Y,ID) then
begin
case ID of
0: begin {user escaped need to remove a popup}
if M.ActiveChain^.NextPtr = nil then
Pullprocesskey := true
else
RemoveTopPopUp(M);
end;
GPullLeft: begin
RemoveAllPopUps(M);
M.MenuDown := true;
PullProcessKey := BarProcessKey(M,331,X,Y,ID);
end;
GPullRight: begin
RemoveAllPopUps(M);
M.MenuDown := true;
PullProcessKey := BarProcessKey(M,333,X,Y,ID);
end;
else {user made a selection}
PullProcessKey := true;
end; {case}
end;
end;
end; { SendtoPopup }
begin
{see if a kwik key was pressed}
ID := HKTopicID(M,K);
if ID <> 0 then
PullprocessKey := true
else
begin
PullProcessKey := false;
Item := AltHotkeyTopicItem(M,K);
if Item <> 0 then {user press main menu level hot key, e.g. Alt-F}
begin
if M.ActiveChain <> nil then
RemoveAllPopUps(M);
DisplayMainItem(M,MainItemPtr(M,M.ActiveItem),M.MainY,false);
M.ActiveItem := Item;
MIP := MainItemPtr(M,Item);
if MIP^.ChildPopUp = nil then {no popup}
begin
ID := MIP^.ID;
PullprocessKey := true
end
else
begin
DisplayMainItem(M,MIP,M.MainY,true);
DrawPopUp(M,MIP^.ChildPopUp,true);
end;
end
else if K = MenuVars.ActivateKey then {user pressed menu activate e.g. F10}
begin
with M do
begin
if ActiveChain <> nil then
RemoveAllPopUps(M);
DisplayMainItem(M,MainItemPtr(M,ActiveItem),M.MainY,true);
end;
end else if K = 500 then
PullProcessKey := PullProcessMouse(M,ID)
else if K = MenuVars.HelpKey then
begin
SaveScreen(InternalScreen3);
M.HelpHook(ActiveMenuID(M));
RestoreScreen(InternalScreen3);
DisposeScreen(InternalScreen3);
end
else if M.ActiveChain = nil then {no popups visible}
PullProcessKey := BarProcessKey(M,K,X,Y,ID)
else {find the active popup}
begin
CP := M.ActiveChain;
while CP^.NextPtr <> nil do
CP := CP^.NextPtr;
SendToPopUp;
end;
end;
M.HindHook(ActiveMenuID(M));
end; { PullProcessKey }
function PullPushKey(var M: Bar; K:word; X,Y:byte): integer;
{Displays the menu and returns the ID of the selected field,
or 0 if user escaped}
var
ID: integer;
FirstIteration: boolean;
CursorX,CursorY,ScanTop,ScanBot: byte;
MsgArea: pointer;
MsgSize: integer;
begin
if WinList <> nil then
ActivateVisibleScreen;
FirstIteration := true;
CursorFind(CursorX,CursorY,ScanTop,ScanBot);
CursorOff;
MsgSize := succ(M.DescX2 - M.DescX1) * 2;
if (M.Style > 4) or (M.Style < 1) then
M.Style := MenuVars.PullStyle;
if GoldMemAvail >= MsgSize then
begin
getmem(MsgArea,MsgSize);
PartSave(M.DescX1,M.DescY,M.DescX2,M.DescY,MsgArea^);
end
else
MsgArea := nil;
M.MsgDisplayed := false;
with M do
DisplayMainItem(M,MainItemPtr(M,ActiveItem),MainY,true);
repeat
if (FirstIteration = false) or ((K=0) and (X=0) and (Y=0)) then
begin
GetInput;
K := KeyVars.LastKey;
X := KeyVars.LastX;
Y := KeyVars.LastY;
end;
FirstIteration := false;
until PullProcessKey (M,K,X,Y,ID);
with M do
DisplayMainItem(M,MainItemPtr(M,ActiveItem),MainY,false);
RemoveAllPopUps(M);
PullPushKey := ID;
if MsgArea <> nil then
begin
PartRestore(M.DescX1,M.DescY,M.DescX2,M.DescY,MsgArea^);
freemem(MsgArea,MsgSize);
end;
CursorPos(CursorX,CursorY);
CursorSize(ScanTop,ScanBot);
if WinList <> nil then
ActivateTopWindow;
end; { PullPushKey }
function ActivatePullMenu(var P: Bar): integer;
{}
begin
ActivatePullMenu := PullPushKey(P,0,0,0);
end; { ActivatePullMenu }
{*********************************************}
{** U N I T I N I T I A L I Z A T I O N **}
{*********************************************}
procedure MenuDefaultSettings;
{}
begin
with MenuVars do
begin
if ColorScreen then
begin
MenuLeft := ' ';
MenuRight := ' ';
PullLeft := ' ';
PullRight := ' ';
end
else
begin
MenuLeft := '';
MenuRight := '';
PullLeft := '';
PullRight := '';
end;
PullSubIndicator := '';
InActiveChar := '!';
TabChar := ':';
ToggleChar := '|';
ActivateKey := 324; {F10}
Separator := '-';
PullStyle := 2;
MsgX1 := 1;
MsgX2 := 80;
MsgY := 25;
HelpStr := ' ~F1~ Help ';
Helpkey := 315;
end;
end; { MenuDefaultSettings }
procedure GoldMenuInit;
{}
begin
with MenuVars do
begin
LastECode := 0;
EMsgFunc := MenuEMsg;
end;
MenuDefaultSettings;
end; {GoldMenuInit}
{$IFDEF TTT5} { allows backward compatibility to TTT5 }
procedure Menu_Set(var M: Menurecord);
begin
MenuSet(M);
with M do
if ColorScreen then
begin
Colors[1] := white;
Colors[2] := blue;
Colors[3] := black;
Colors[4] := lightgray;
Colors[5] := blue;
end else
begin
Colors[1] := white;
Colors[2] := black;
Colors[3] := lightgray;
Colors[4] := black;
Colors[5] := white;
end;
end; {Menu_Set}
procedure Display_Menu(MenuDef: Menurecord; Window:Boolean; var Choice,Errorcode: integer);
{}
begin
with MenuDef do
begin
Colors[5] := CAttr(Colors[5],Colors[4]);
end;
DisplayMenu(MenuDef,Window,Choice,Errorcode);
end; {Display_Menu}
{$ENDIF}
begin
GoldMenuInit;
end.